using System;
using UnityEngine;
using System.Collections;

public class MazeMesh
{
	public GameObject [] mazeMeshs;
	private Mesh [] meshs;	
	public GameObject [] lavaMazeMeshs;
	private Mesh [] lavaMeshs;	
	public GameObject [] finishMazeMeshs;
	private Mesh [] finishMeshs;
	private float cellSize;	
	private int width, height;
	private int numberOfMeshs;
	private	int numMeshsX;
	private int numMeshsY;
	
      	// List of squares we are going to make solid - we use a list, instead of immediately so as not to corrupt the data we're working on
    private ArrayList makeSolid = new ArrayList();
      
	
	public MazeMesh ()
	{
	}
	
	public void changeSquareColor(MazeLocation mazeLocation, Color color)
	{
		int meshX = (int)((float)mazeLocation.x / (width/numMeshsX));
		int meshY = (int)((float)mazeLocation.z / (height/numMeshsY));
		if (mazeMeshs[meshX + meshY*numMeshsY] != null)
			mazeMeshs[meshX + meshY*numMeshsY].renderer.material.color = color;
	}
	
	public void resetSquareColors()
	{
		foreach(GameObject mesh in mazeMeshs)
		{
			if (mesh != null)
				mesh.renderer.material.color = Color.gray;
		}
	}
	
	//width is number of cells in x direction
	//height is number of cells in z direction
	public void fillMeshSquares(Maze maze, float cellSize, MazeSquare [] cells, int width, int height)
	{
		numMeshsX = width/4;
		numMeshsY = height/4;
		numberOfMeshs = numMeshsX*numMeshsY;
		int squaresPerMesh = (width*height)/numberOfMeshs;	
		meshs = new Mesh[numberOfMeshs];
		mazeMeshs = new GameObject[numberOfMeshs];
		
		lavaMeshs = new Mesh[numberOfMeshs];
		lavaMazeMeshs = new GameObject[numberOfMeshs];
		
		finishMeshs = new Mesh[numberOfMeshs];
		finishMazeMeshs = new GameObject[numberOfMeshs];	
		this.width = width;
		this.height = height;
		this.cellSize = cellSize;
		
		for (int mj = 0; mj < numMeshsY; mj++)
		{
			for (int mi = 0;  mi < numMeshsX; mi++)
			{
				int meshNum = mi + numMeshsX * mj;
				//Create Vertex List - We use unique vertices for each face because there are only vertex normals and we want a faceted look
				ArrayList verts = new ArrayList();
				ArrayList uvs = new ArrayList();
				ArrayList normals = new ArrayList();
				ArrayList tris = new ArrayList();
				
				ArrayList lavaVerts = new ArrayList();
				ArrayList lavaUvs = new ArrayList();
				ArrayList lavaNormals = new ArrayList();
				ArrayList lavaTris = new ArrayList();
				
				ArrayList finishVerts = new ArrayList();
				ArrayList finishUvs = new ArrayList();
				ArrayList finishNormals = new ArrayList();
				ArrayList finishTris = new ArrayList();
				
				for (int i = mj * (height/numMeshsY); i < (mj+1) * (height/numMeshsY); i++)
				{
					for (int j = mi * (width/numMeshsX); j < (mi+1) * (width/numMeshsX); j++)
					{
						ArrayList currVerts, currUvs, currNormals, currColors, currTris;
						if (isCellFinish(cells, j, i))
						{
							currVerts = finishVerts;	
							currUvs = finishUvs;
							currNormals = finishNormals;
							currTris = finishTris;	
						}else if (isCellLava(cells, j,i))
						{
							currVerts = lavaVerts;	
							currUvs = lavaUvs;
							currNormals = lavaNormals;
							currTris = lavaTris;
						}else
						{
							currVerts = verts;	
							currUvs = uvs;
							currNormals = normals;
							currTris = tris;
						}
						if (!isCellSolid(cells, j, i))
						{
							if (isCellSolid(cells, j, i+1) && isCellSolid(cells, j+1, i) && !isCellSolid(cells, j, i-1) && !isCellSolid(cells, j-1, i)) 
							{
								addCorner(j, i, 0, currVerts, currUvs, currNormals, currTris); 
								makeSolid.Add( maze.getMazeSquare(new MazeLocation(j, i)) );
							}
							else if (!isCellSolid(cells, j, i+1) && isCellSolid(cells, j+1, i) && isCellSolid(cells, j, i-1) && !isCellSolid(cells, j-1, i)) 
							{
								addCorner(j, i, 1, currVerts, currUvs, currNormals, currTris); 
								makeSolid.Add( maze.getMazeSquare(new MazeLocation(j, i)) );
							}
							else if (!isCellSolid(cells, j, i+1) && !isCellSolid(cells, j+1, i) && isCellSolid(cells, j, i-1) && isCellSolid(cells, j-1, i)) 
							{
								addCorner(j, i, 2, currVerts, currUvs, currNormals, currTris); 
								makeSolid.Add( maze.getMazeSquare(new MazeLocation(j, i)) );
							}
							else if (isCellSolid(cells, j, i+1) && !isCellSolid(cells, j+1, i) && !isCellSolid(cells, j, i-1) && isCellSolid(cells, j-1, i)) 
							{
								addCorner(j, i, 3, currVerts, currUvs, currNormals, currTris); 
								makeSolid.Add( maze.getMazeSquare(new MazeLocation(j, i)) );
							}
							else
							{
								addFloor(j, i, currVerts, currUvs, currNormals, currTris);
								if (isCellSolid(cells, j, i-1))
									addWall(j, i, 0, currVerts, currUvs, currNormals, currTris);
								if (isCellSolid(cells, j, i+1))
									addWall(j, i, 2, currVerts, currUvs, currNormals, currTris);
								if (isCellSolid(cells, j+1, i))
									addWall(j, i, 3, currVerts, currUvs, currNormals, currTris);
								if (isCellSolid(cells, j-1, i))
									addWall(j, i, 1, currVerts, currUvs, currNormals, currTris);
							}
							
							//Set corners as solid for pathfinding
							/*	TODO
								if (isCellSolid(cells, j, i+1) && isCellSolid(cells, j+1, i) && !isCellSolid(cells, j, i-1) && !isCellSolid(cells, j-1, i)) 
									maze.getMazeSquare(i, j).setSolid(true);	
								else if (!isCellSolid(cells, j, i+1) && isCellSolid(cells, j+1, i) && isCellSolid(cells, j, i-1) && !isCellSolid(cells, j-1, i)) 
									maze.getMazeSquare(i, j).setSolid(true);	
								else if (!isCellSolid(cells, j, i+1) && !isCellSolid(cells, j+1, i) && isCellSolid(cells, j, i-1) && isCellSolid(cells, j-1, i)) 
									maze.getMazeSquare(i, j).setSolid(true);	
								else if (isCellSolid(cells, j, i+1) && !isCellSolid(cells, j+1, i) && !isCellSolid(cells, j, i-1) && isCellSolid(cells, j-1, i)) 
									maze.getMazeSquare(i, j).setSolid(true);	
							*/
							
							
						}
						else
						{
							//addRoof(j,i,tris);
						}
					}
				}
				
				if (verts.Count	> 0)
				{
					meshs[meshNum] = new Mesh();
					mazeMeshs[meshNum] = new GameObject("Maze mesh: " + meshNum);
					mazeMeshs[meshNum].AddComponent("MeshFilter");
					mazeMeshs[meshNum].AddComponent("MeshRenderer");
					((MeshFilter)mazeMeshs[meshNum].GetComponent("MeshFilter")).mesh = meshs[meshNum];
					mazeMeshs[meshNum].renderer.material.color = Color.gray;	
					mazeMeshs[meshNum].renderer.material.shader = Shader.Find("Specular");
	
					meshs[meshNum].vertices = (Vector3[])verts.ToArray(typeof(Vector3));
					meshs[meshNum].uv = (Vector2[])uvs.ToArray(typeof(Vector2));
					meshs[meshNum].triangles = (int[])tris.ToArray(typeof(int));	
					meshs[meshNum].normals = (Vector3[])normals.ToArray(typeof(Vector3));
					meshs[meshNum].Optimize();
					// Add new collision mesh
					mazeMeshs[meshNum].AddComponent("MeshCollider");
				}
				
				if (finishVerts.Count	> 0)
				{
					finishMeshs[meshNum] = new Mesh();
					finishMazeMeshs[meshNum] = new GameObject("Maze finish mesh: " + meshNum);
					finishMazeMeshs[meshNum].AddComponent("MeshFilter");
					finishMazeMeshs[meshNum].AddComponent("MeshRenderer");
					((MeshFilter)finishMazeMeshs[meshNum].GetComponent("MeshFilter")).mesh = finishMeshs[meshNum];
					finishMazeMeshs[meshNum].renderer.material.color = Color.cyan;	
					finishMazeMeshs[meshNum].renderer.material.shader = Shader.Find("Specular");
					
					finishMeshs[meshNum].vertices = (Vector3[])finishVerts.ToArray(typeof(Vector3));
					finishMeshs[meshNum].uv = (Vector2[])finishUvs.ToArray(typeof(Vector2));
					finishMeshs[meshNum].triangles = (int[])finishTris.ToArray(typeof(int));	
					finishMeshs[meshNum].normals = (Vector3[])finishNormals.ToArray(typeof(Vector3));
					finishMeshs[meshNum].Optimize();
					// Add new collision mesh
					finishMazeMeshs[meshNum].AddComponent("MeshCollider");
				}	
					
				if (lavaVerts.Count	> 0)
				{
				
					lavaMeshs[meshNum] = new Mesh();
					lavaMazeMeshs[meshNum] = new GameObject("Maze lava mesh: " + meshNum);
					lavaMazeMeshs[meshNum].AddComponent("MeshFilter");
					lavaMazeMeshs[meshNum].AddComponent("MeshRenderer");
					((MeshFilter)lavaMazeMeshs[meshNum].GetComponent("MeshFilter")).mesh = lavaMeshs[meshNum];
					lavaMazeMeshs[meshNum].renderer.material.color = Color.red;	
					lavaMazeMeshs[meshNum].renderer.material.shader = Shader.Find("Specular");
					
					lavaMeshs[meshNum].vertices = (Vector3[])lavaVerts.ToArray(typeof(Vector3));
					lavaMeshs[meshNum].uv = (Vector2[])lavaUvs.ToArray(typeof(Vector2));
					lavaMeshs[meshNum].triangles = (int[])lavaTris.ToArray(typeof(int));	
					lavaMeshs[meshNum].normals = (Vector3[])lavaNormals.ToArray(typeof(Vector3));
					lavaMeshs[meshNum].Optimize();
					// Add new collision mesh
					lavaMazeMeshs[meshNum].AddComponent("MeshCollider");
				}
			}
			
		}
      
      /*
		for(int j = 0; j < height; j++)
		{

			for(int i = 0; i < width; i++)

			{
				MazeLocation mazeLocation = new MazeLocation(i,j);
			
				//Set corners as solid for pathfinding

				if (isCellSolid(cells, j, i+1) && isCellSolid(cells, j+1, i) && !isCellSolid(cells, j, i-1) && !isCellSolid(cells, j-1, i)) 
					makeSolid.Add(maze.getMazeSquare(mazeLocation));	
				else if (!isCellSolid(cells, j, i+1) && isCellSolid(cells, j+1, i) && isCellSolid(cells, j, i-1) && !isCellSolid(cells, j-1, i)) 
					makeSolid.Add(maze.getMazeSquare(mazeLocation));	
				else if (!isCellSolid(cells, j, i+1) && !isCellSolid(cells, j+1, i) && isCellSolid(cells, j, i-1) && isCellSolid(cells, j-1, i)) 
					makeSolid.Add(maze.getMazeSquare(mazeLocation));	
				else if (isCellSolid(cells, j, i+1) && !isCellSolid(cells, j+1, i) && !isCellSolid(cells, j, i-1) && isCellSolid(cells, j-1, i)) 
					makeSolid.Add(maze.getMazeSquare(mazeLocation));	

			}

		}*/
		
        foreach(MazeSquare square in makeSolid)
        {
			square.setSolid(true);	
		}
	

		      
      
	}
	
	private void addCorner(int x, int z, int rotation, ArrayList verts,ArrayList uvs, ArrayList normals, ArrayList tris) 
	{
		
		int vertCount = verts.Count;
		Vector3 translateVector = new Vector3(x * cellSize, 0, z *cellSize);			
		//Default rotation is wall in the north-east corner
		
		//Floor 
		Vector3 vert1 = new Vector3(-0.5f, 0.0f, -0.5f);
		Vector3 vert3 = new Vector3(0.5f, 0.0f, -0.5f);
		Vector3 vert2 = new Vector3(-0.5f, 0.0f, 0.5f);
		
		//Wall
		Vector3 wallvert1 = new Vector3(-0.5f, 0.0f, 0.5f);
		Vector3 wallvert3 = new Vector3(0.5f, 0.0f, -0.5f);
		Vector3 wallvert2 = new Vector3(-0.5f, 1.0f, 0.5f);
		
		Vector3 wallvert4 = new Vector3(0.5f, 0.0f, -0.5f);
		Vector3 wallvert6 = new Vector3(0.5f, 1.0f, -0.5f);
		Vector3 wallvert5 = new Vector3(-0.5f, 1.0f, 0.5f);
		
		rotateVector3(ref vert1, rotation);
		rotateVector3(ref vert2, rotation);
		rotateVector3(ref vert3, rotation);
		
		rotateVector3(ref wallvert1, rotation);
		rotateVector3(ref wallvert2, rotation);
		rotateVector3(ref wallvert3, rotation);
		
		rotateVector3(ref wallvert4, rotation);
		rotateVector3(ref wallvert5, rotation);
		rotateVector3(ref wallvert6, rotation);	

		
		vert1 *= cellSize;
		vert2 *= cellSize;
		vert3 *= cellSize;
		
		wallvert1 *= cellSize;
		wallvert2 *= cellSize;
		wallvert3 *= cellSize;	
		wallvert4 *= cellSize;
		wallvert5 *= cellSize;
		wallvert6 *= cellSize;	
			
		vert1 += translateVector;
		vert2 += translateVector;
		vert3 += translateVector;
		
		wallvert1 += translateVector;
		wallvert2 += translateVector;
		wallvert3 += translateVector;	
		wallvert4 += translateVector;
		wallvert5 += translateVector;
		wallvert6 += translateVector;	
			
		verts.Add(vert1);
		verts.Add(vert2);
		verts.Add(vert3);
		
		verts.Add(wallvert1);
		verts.Add(wallvert2);
		verts.Add(wallvert3);
		
		verts.Add(wallvert4);
		verts.Add(wallvert5);
		verts.Add(wallvert6);
		
		//Add normals
		for (int i = 0; i < 3; i++)
		{
			normals.Add(Vector3.up);
		}	
		//Add normals
		Vector3 wallNormal = new Vector3(-1.0f, 0.0f, -1.0f);
		rotateVector3(ref wallNormal, rotation);
		wallNormal.Normalize();
		for (int i = 0; i < 6; i++)
		{
			normals.Add(wallNormal);
		}	
		
		
		Vector2 uv1 = new Vector2(0.0f, 0.0f);
		Vector2 uv2 = new Vector2(1.0f, 0.0f);
		Vector2 uv3 = new Vector2(0.0f, 1.0f);
		
		uvs.Add(uv1);	
		uvs.Add(uv2);		
		uvs.Add(uv3);		
		
		Vector2 walluv1 = new Vector2(0.0f, 0.0f);
		Vector2 walluv2 = new Vector2(1.0f, 0.0f);
		Vector2 walluv3 = new Vector2(0.0f, 1.0f);
		
		Vector2 walluv4 = new Vector2(1.0f, 0.0f);
		Vector2 walluv5 = new Vector2(1.0f, 1.0f);
		Vector2 walluv6 = new Vector2(0.0f, 1.0f);
		
		uvs.Add(walluv1);	
		uvs.Add(walluv2);	
		uvs.Add(walluv3);	
		uvs.Add(walluv4);	
		uvs.Add(walluv5);	
		uvs.Add(walluv6);		
		
		tris.Add(vertCount+0);	
		tris.Add(vertCount+1);	
		tris.Add(vertCount+2);	
		
		tris.Add(vertCount+3);	
		tris.Add(vertCount+4);	
		tris.Add(vertCount+5);		
		
		tris.Add(vertCount+6);	
		tris.Add(vertCount+7);	
		tris.Add(vertCount+8);		
		
	}
	
	private void addFloor(int x, int z, ArrayList verts, ArrayList uvs, ArrayList normals, ArrayList tris)
	{
		int vertCount = verts.Count;
		Vector3 translateVector = new Vector3(x * cellSize, 0, z *cellSize);			
		
		//Add verts 
		Vector3 vert1 = new Vector3(-0.5f, 0.0f, -0.5f);
		Vector3 vert3 = new Vector3(0.5f, 0.0f, -0.5f);
		Vector3 vert2 = new Vector3(-0.5f, 0.0f, 0.5f);
		
		Vector3 vert4 = new Vector3(0.5f, 0.0f, -0.5f);
		Vector3 vert6 = new Vector3(0.5f, 0.0f, 0.5f);
		Vector3 vert5 = new Vector3(-0.5f, 0.0f, 0.5f);
		
		
		vert1 *= cellSize;
		vert2 *= cellSize;
		vert3 *= cellSize;
		
		vert4 *= cellSize;
		vert5 *= cellSize;
		vert6 *= cellSize;	
		
		vert1 += translateVector;
		vert2 += translateVector;
		vert3 += translateVector;
		
		vert4 += translateVector;
		vert5 += translateVector;
		vert6 += translateVector;	
		
		verts.Add(vert1);
		verts.Add(vert2);
		verts.Add(vert3);
		verts.Add(vert4);
		verts.Add(vert5);
		verts.Add(vert6);	
		
		//Add normals
		for (int i = 0; i < 6; i++)
		{
			normals.Add(Vector3.up);
		}
		
		//Add uvs
		Vector2 uv1 = new Vector2(0.0f, 0.0f);
		Vector2 uv2 = new Vector2(1.0f, 0.0f);
		Vector2 uv3 = new Vector2(0.0f, 1.0f);
		
		Vector2 uv4 = new Vector2(1.0f, 0.0f);
		Vector2 uv5 = new Vector2(1.0f, 1.0f);
		Vector2 uv6 = new Vector2(0.0f, 1.0f);
		
		uvs.Add(uv1);	
		uvs.Add(uv2);	
		uvs.Add(uv3);	
		uvs.Add(uv4);	
		uvs.Add(uv5);	
		uvs.Add(uv6);
		
		tris.Add(vertCount+0);	
		tris.Add(vertCount+1);	
		tris.Add(vertCount+2);	
		
		tris.Add(vertCount+3);	
		tris.Add(vertCount+4);	
		tris.Add(vertCount+5);	
	}
	
	private void addWall(int x, int z, int rotation, ArrayList verts, ArrayList uvs, ArrayList normals, ArrayList tris)
	{ 
		//rotation can be 0 90 180 270
		if (rotation < 0 || rotation > 3)
			return;
		
		int vertCount = verts.Count;
		//Vector3 scaleVector = new Vector3(cellSize, cellSize, cellSize);
		Vector3 translateVector = new Vector3(x * cellSize, 0, z *cellSize);			
		
		Vector3 vert1;
		Vector3 vert2;
		Vector3 vert3;
		
		Vector3 vert4;
		Vector3 vert5;
		Vector3 vert6;	
		//Add verts 
		vert1 = new Vector3(-0.5f, 0.0f, 0.0f);
		vert2 = new Vector3(0.5f, 0.0f, 0.0f);
		vert3 = new Vector3(-0.5f, 1.0f, 0.0f);
		
		vert4 = new Vector3(0.5f, 0.0f, 0.0f);
		vert5 = new Vector3(0.5f, 1.0f, 0.0f);
		vert6 = new Vector3(-0.5f, 1.0f, 0.0f);
		
		rotateVector3(ref vert1, rotation);
		rotateVector3(ref vert2, rotation);
		rotateVector3(ref vert3, rotation);
		
		rotateVector3(ref vert4, rotation);
		rotateVector3(ref vert5, rotation);
		rotateVector3(ref vert6, rotation);
		
		vert1 *= cellSize;
		vert2 *= cellSize;
		vert3 *= cellSize;
		
		vert4 *= cellSize;
		vert5 *= cellSize;
		vert6 *= cellSize;
		//vert6.Scale(scaleVector);
		
		if (rotation == 0)
		{
			translateVector.x += cellSize * 0.0f;
			translateVector.z -= cellSize * 0.5f;
		}
		if (rotation == 1)
		{
			translateVector.x -= cellSize * 0.5f;
			translateVector.z += cellSize * 0.0f;
		}
		if (rotation == 2)
		{
			translateVector.x += cellSize * 0.0f;
			translateVector.z += cellSize * 0.5f;
		}
		if (rotation == 3)
		{
			translateVector.x += cellSize * 0.5f;
			translateVector.z += cellSize * 0.0f;	
		}
		
		vert1 += translateVector;
		vert2 += translateVector;
		vert3 += translateVector;
		
		vert4 += translateVector;
		vert5 += translateVector;
		vert6 += translateVector;
		
		verts.Add(vert1);
		verts.Add(vert2);
		verts.Add(vert3);
		verts.Add(vert4);
		verts.Add(vert5);
		verts.Add(vert6);
		
		//Add uvs
		Vector2 uv1 = new Vector2(0.0f, 0.0f);
		Vector2 uv2 = new Vector2(1.0f, 0.0f);
		Vector2 uv3 = new Vector2(0.0f, 1.0f);
		
		Vector2 uv4 = new Vector2(1.0f, 0.0f);
		Vector2 uv5 = new Vector2(1.0f, 1.0f);
		Vector2 uv6 = new Vector2(0.0f, 1.0f);
		
		uvs.Add(uv1);	
		uvs.Add(uv2);	
		uvs.Add(uv3);	
		uvs.Add(uv4);	
		uvs.Add(uv5);	
		uvs.Add(uv6);	
		
		//Add normals
		for (int i = 0; i < 6; i++)
		{
			if (rotation == 0)
			{
				normals.Add(new Vector3(0.0f, 0.0f,1.0f));
			}
			if (rotation == 1)
			{
				normals.Add(new Vector3(1.0f, 0.0f,0.0f));
			}
			if (rotation == 2)
			{
				normals.Add(new Vector3(0.0f, 0.0f,-1.0f));
			}
			if (rotation == 3)
			{
				normals.Add(new Vector3(-1.0f, 0.0f,0.0f));
			}
		}
		
		//Add tris
		tris.Add(vertCount+0);	
		tris.Add(vertCount+1);	
		tris.Add(vertCount+2);	
		
		tris.Add(vertCount+3);	
		tris.Add(vertCount+4);	
		tris.Add(vertCount+5);	

	}
	
	
	// rotates a vector about the origin around the y axis in 90 degree increaments
	void rotateVector3(ref Vector3 vec, int rotation)
	{
		//rotation can be 0 90 180 270
		if (rotation < 0 || rotation > 3)
			return;
		
		Vector3 returnVec = new Vector3(vec.x, vec.y, vec.z);
		
		if (rotation == 0)
			return; // no rotation
		if (rotation == 1)
		{
			returnVec.x = vec.z;
			returnVec.z = -vec.x;
		}
		if (rotation == 2)
		{
			returnVec.x = -vec.x;
			returnVec.z = -vec.z;
		}
		if (rotation == 3)
		{
			returnVec.x = -vec.z;
			returnVec.z = vec.x;
		}
		vec.x = returnVec.x;
		vec.y = returnVec.y;
		vec.z = returnVec.z;
	}

	private bool isCellSolid(MazeSquare [] cells, int x, int z)
	{
		if (x < 0 || x >= width || z < 0 || z >= height)
			return true; // outside region is solid
		else
			return cells[x + z*width].isSolid(); 
	}	
	
	private bool isCellFinish(MazeSquare [] cells, int x, int z)
	{
		if (x < 0 || x >= width || z < 0 || z >= height)
			return false;
		else
			return cells[x + z*width].isFinish(); 
	}	
	
	private bool isCellLava(MazeSquare [] cells, int x, int z)
	{
		if (x < 0 || x >= width || z < 0 || z >= height)
			return false;
		else
			return cells[x + z*width].isLava(); 
	}	
}

